package com.hiflying.commons.utils;

public class CrcCalculator {
    public static final byte[] TestBytes = {49, 50, 51, 52, 53, 54, 55, 56, 57};
    public byte HashSize = 8;
    public AlgoParams Parameters;
    private long _mask = -1;
    private long[] _table = new long[256];

    public static class Crc8 {
        public static AlgoParams Crc8 = new AlgoParams("CRC-8", 8, 7, 0, false, false, 0, 244);
        public static AlgoParams Crc8Cdma2000 = new AlgoParams("CRC-8/CDMA2000", 8, 155, 255, false, false, 0, 218);
        public static AlgoParams Crc8Darc = new AlgoParams("CRC-8/DARC", 8, 57, 0, true, true, 0, 21);
        public static AlgoParams Crc8DvbS2 = new AlgoParams("CRC-8/DVB-S2", 8, 213, 0, false, false, 0, 188);
        public static AlgoParams Crc8Ebu = new AlgoParams("CRC-8/EBU", 8, 29, 255, true, true, 0, 151);
        public static AlgoParams Crc8ICode = new AlgoParams("CRC-8/I-CODE", 8, 29, 253, false, false, 0, 126);
        public static AlgoParams Crc8Itu = new AlgoParams("CRC-8/ITU", 8, 7, 0, false, false, 85, 161);
        public static AlgoParams Crc8Maxim = new AlgoParams("CRC-8/MAXIM", 8, 49, 0, true, true, 0, 161);
        public static AlgoParams Crc8Rohc = new AlgoParams("CRC-8/ROHC", 8, 7, 255, true, true, 0, 208);
        public static AlgoParams Crc8Wcdma = new AlgoParams("CRC-8/WCDMA", 8, 155, 0, true, true, 0, 37);
        public static final AlgoParams[] Params = {Crc8, Crc8Cdma2000, Crc8Darc, Crc8DvbS2, Crc8Ebu, Crc8ICode, Crc8Itu, Crc8Maxim, Crc8Rohc, Crc8Wcdma};
    }

    public CrcCalculator(AlgoParams params) {
        this.Parameters = params;
        this.HashSize = (byte) params.HashSize;
        if (this.HashSize < 64) {
            this._mask = (1 << this.HashSize) - 1;
        }
        CreateTable();
    }

    public long calc(byte[] data, int offset, int length) {
        return (this.Parameters.XorOut ^ ComputeCrc(this.Parameters.RefOut ? ReverseBits(this.Parameters.Init, this.HashSize) : this.Parameters.Init, data, offset, length)) & this._mask;
    }

    private long ComputeCrc(long init, byte[] data, int offset, int length) {
        long crc = init;
        if (this.Parameters.RefOut) {
            for (int i = offset; i < offset + length; i++) {
                crc = (this._table[(int) ((((long) data[i]) ^ crc) & 255)] ^ (crc >>> 8)) & this._mask;
            }
        } else {
            int toRight = this.HashSize - 8;
            if (toRight < 0) {
                toRight = 0;
            }
            for (int i2 = offset; i2 < offset + length; i2++) {
                crc = (this._table[(int) (((crc >> toRight) ^ ((long) data[i2])) & 255)] ^ (crc << 8)) & this._mask;
            }
        }
        return crc;
    }

    private void CreateTable() {
        for (int i = 0; i < this._table.length; i++) {
            this._table[i] = CreateTableEntry(i);
        }
    }

    private long CreateTableEntry(int index) {
        long r = (long) index;
        if (this.Parameters.RefIn) {
            r = ReverseBits(r, this.HashSize);
        } else if (this.HashSize > 8) {
            r <<= this.HashSize - 8;
        }
        long lastBit = 1 << (this.HashSize - 1);
        for (int i = 0; i < 8; i++) {
            if ((r & lastBit) != 0) {
                r = (r << 1) ^ this.Parameters.Poly;
            } else {
                r <<= 1;
            }
        }
        if (this.Parameters.RefOut) {
            r = ReverseBits(r, this.HashSize);
        }
        return this._mask & r;
    }

    static long ReverseBits(long ul, int valueLength) {
        long newValue = 0;
        for (int i = valueLength - 1; i >= 0; i--) {
            newValue |= (1 & ul) << i;
            ul >>= 1;
        }
        return newValue;
    }

    public static class AlgoParams {
        public long Check;
        public int HashSize;
        public long Init;
        public String Name;
        public long Poly;
        public boolean RefIn;
        public boolean RefOut;
        public long XorOut;

        public AlgoParams(String name, int hashSize, long poly, long init, boolean refIn, boolean refOut, long xorOut, long check) {
            this.Name = name;
            this.Check = check;
            this.Init = init;
            this.Poly = poly;
            this.RefIn = refIn;
            this.RefOut = refOut;
            this.XorOut = xorOut;
            this.HashSize = hashSize;
        }
    }
}
